# -*- coding: utf-8 -*-
"""
@Time    : 2021/3/23 9:56
@Author  : hui lin
"""
from __future__ import annotations

import os

import pygame
import random

from pygame.font import Font

from client.config import ClientConfig
from client.model.entity_base import EntityBase
from client.model.weapon.weapon import Weapon
from package.static.const import MoveDirection, Faction, TypeIdConst, GroupIdConst
from package.posi import Position, get_tar_position
from package.universe import universe, TankDogma, TypeItem, EsException
from pygame import Surface, Rect
from typing import Optional, List, TYPE_CHECKING


if TYPE_CHECKING:
    from client.model.weapon.bullet import Bullet


class TankBase(EntityBase):

    def __init__(self, type_id: TypeIdConst, faction: Faction, position: Position, **kwargs):
        super(TankBase, self).__init__(type_id=type_id, **kwargs)
        self.dogma: TankDogma = universe.get_tank_special_info(type_id)
        self._faction: Faction = faction

        # 缓存的行进距离
        self._cache_move: float = 0
        # 最小移动距离
        self.min_move: int = 4

        self.direction = MoveDirection.UP
        if "direction" in kwargs:
            self.direction: MoveDirection = kwargs["direction"]

        self.next_direction: MoveDirection = MoveDirection.NONE
        # 按下一次方向移动的冷却时间（帧数）
        self.cool_move_next: int = 0

        self.image_path: str = self.get_default_image_path()
        if "image" in kwargs:
            self.image_path: str = kwargs["image"]

        # 坦克图片对象（包括所有方向）
        self.tank_image: Surface = pygame.image.load(self.image_path).convert_alpha()
        # 当前朝向坦克图片集对象(包括特效)
        self.tank_direction_image: Optional[Surface] = None
        self.set_direction(self.direction)

        # 坦克在移动时，模拟移动效果，多张图片轮番切换
        self._cur_image_index = 0
        self._cur_image: Optional[Surface] = None
        self.set_cur_image()
        self.rect: Optional[Rect] = self._cur_image.get_rect()
        self._init_position(position)

        # 装备的武器
        self.weapons: List[Weapon] = [Weapon(self.dogma.weapon, self._faction)]

        self.record_scene = {}

        self.need_remove: bool = False
        self.dead_duration_time = 6
        root_dir = ClientConfig().root_dir
        self.dead_image = pygame.image.load(os.path.join(root_dir, self.dogma.dead_image)).convert_alpha()

        # 受伤播放效果
        self.hurt_duration_effect: int = 0
        self.hurt_effect = pygame.image.load(os.path.join(root_dir, self.dogma.hurt_effect)).convert_alpha()

        self.blood_txt: Font = pygame.font.SysFont("Arial", 10)
        self.blood_txt.set_bold(False)

    def __str__(self):
        return "<{} cur_direction={} next_direction={} " \
               "cur_raw_speed={} cache_move={}>"\
            .format(
                self.__class__.__name__,
                self.direction, self.next_direction, self._cur_raw_speed, self._cache_move
            )

    __repr__ = __str__

    def get_blood_color(self):
        if Faction.OUR_FACTION == self._faction:
            color = (255, 0, 0)
        else:
            color = (255, 0, 255)
        return color

    def get_blood_position(self):
        fix_x, fix_y = 0, 0
        if MoveDirection.UP == self.direction:
            fix_x = 30
        elif MoveDirection.DOWN == self.direction:
            fix_x = 30
            fix_y = -10
        elif MoveDirection.LEFT == self.direction:
            fix_x = 30
            fix_y = -10
        elif MoveDirection.LEFT == self.direction:
            fix_x = 30
            fix_y = -10
        else:
            fix_x = 30
            fix_y = -10
        return self.rect.x + fix_x, self.rect.y + fix_y

    def save_cur_scene(self):
        self.record_scene["cache_move"]: MoveDirection = self._cache_move
        self.record_scene["rect"]: Rect = self.rect

    def resume_scene(self):
        # print("resume:", self.record_scene, self._cache_move, self.rect)
        if "cache_move" in self.record_scene:
            self._cache_move = self.record_scene["cache_move"]
        if "rect" in self.record_scene:
            self.rect = self.record_scene["rect"]

    def get_default_image_path(self) -> str:
        info = universe.get_type_base_info(type_id=self._type_id)
        if Faction.OUR_FACTION == self._faction:
            image = info.image[self._faction][-1]
        else:
            image = random.choice(info.image.get(self._faction, Faction.NONE))
        root_dir = ClientConfig().root_dir
        image = os.path.join(root_dir, image)
        return image

    def set_cur_image(self):
        if self.is_dead:
            self._cur_image = self.dead_image
            return

        if not self.real_speed:
            # 静止状态
            self._cur_image_index = 0
            index = self._cur_image_index
        else:
            self._cur_image_index += 1
            self._cur_image_index = self._cur_image_index % 5
            index = 1 if 0 != self._cur_image_index % 5 else 0
        self._cur_image = self.tank_direction_image.subsurface(
            (48 * int(index), 0), (48, 48))

    def set_direction(self, direction: MoveDirection) -> None:
        if direction != self.direction:
            self._cache_move = 0
        if direction == MoveDirection.UP:
            self.tank_direction_image = self.tank_image.subsurface((0, 0), (96, 48))
        elif direction == MoveDirection.DOWN:
            self.tank_direction_image = self.tank_image.subsurface((0, 48), (96, 48))
        elif direction == MoveDirection.LEFT:
            self.tank_direction_image = self.tank_image.subsurface((0, 96), (96, 48))
        elif direction == MoveDirection.RIGHT:
            self.tank_direction_image = self.tank_image.subsurface((0, 144), (96, 48))
        self.direction = direction

    def set_next_direction(self, direction: MoveDirection) -> None:
        self.next_direction = direction

    def _init_position(self, position: Position):
        self.rect.left, self.rect.top = position.x, position.y

    def get_position(self) -> Position:
        return Position(self.rect.left, self.rect.top)

    def _move(self, x: int, y: int):
        self.rect = self.rect.move(x, y)

    def upgrade(self, new_type_id: TypeIdConst) -> None:
        # 坦克升级
        if GroupIdConst.TANK != universe.get_group_id(new_type_id):
            raise EsException("TypeIdError")
        if new_type_id == self._type_id:
            return
        self._type_id = new_type_id
        new_info: TypeItem = universe.get_type_base_info(new_type_id)
        self._cur_blood += (new_info.max_blood - self.static_info.max_blood)
        self.static_info = new_info
        self.dogma_info: TankDogma = universe.get_tank_special_info(new_type_id)
        self.weapons: List[Weapon] = [Weapon(self.dogma.weapon, self._faction)]

    def is_stop(self):
        return 0 == int(self._cur_raw_speed)

    def stop_move(self):
        self._cur_raw_speed = 0
        self._cache_move = 0

    def set_move(self, direction: List[MoveDirection]):
        if direction and self.direction not in direction:
            # 方向不一致时先改变方向
            self.set_direction(direction[0])
        if 2 == len(direction):
            self.next_direction = direction[1] if direction[0] == self.direction else direction[0]
        else:
            self.next_direction = MoveDirection.NONE
        if direction:
            self._cur_raw_speed = self.static_info.max_speed
        else:
            self.stop_move()

    def try_move_cur(self, frame_physical_rate: int) -> int:
        """
        尝试往当前方向移动
        :param frame_physical_rate: 帧率
        :return: int, 0/1 是否发生了移动
        """
        # if frame_physical_rate % 5 != 0:
        #     return 0
        interval = 1.0 / frame_physical_rate
        real_speed = self.real_speed

        if 0.001 < real_speed and self.direction:
            if self.direction in [MoveDirection.UP, MoveDirection.LEFT]:
                new_move = -1.0 * interval * real_speed
            else:
                new_move = 1.0 * interval * real_speed
            max_move = self._cache_move + new_move
            # 移动距离 = （理论最大距离 // 最小格子数）* 格子像素
            real_move: int = int(max_move // self.min_move) * self.min_move
            self._cache_move = max_move - real_move

            if self.direction in [MoveDirection.UP, MoveDirection.DOWN]:
                self._move(0, real_move)
            else:
                self._move(real_move, 0)
            if real_move:
                return 1
        return 0

    def try_move_next(self) -> int:
        """
        尝试往下一个方向移动（优先级大于当前方向：即如果该方向可以移动，则超此处移动）
        :return: int, 0/1 是否发生了移动
        """
        if 0 < self.cool_move_next:
            return 0
        real_speed = self.real_speed

        if 0.001 < real_speed and self.next_direction:
            if self.next_direction in [MoveDirection.UP, MoveDirection.LEFT]:
                new_move = - self.min_move
            else:
                new_move = self.min_move
            if self.next_direction in [MoveDirection.UP, MoveDirection.DOWN]:
                self._move(0, new_move)
            else:
                self._move(new_move, 0)
            return 1
        return 0

    def start_cool_move_next(self, frame_physical_rate: int):
        speed = 1.0 / frame_physical_rate * self.real_speed
        self.cool_move_next = int(self.min_move // speed) + 1

    def draw(self, screen: Surface) -> None:
        self.set_cur_image()
        screen.blit(self._cur_image, self.rect)

        blood_color = self.get_blood_color()
        frame_surface = self.blood_txt.render(f"{self._cur_blood}", True, blood_color)

        x, y = self.get_blood_position()
        screen.blit(frame_surface, (x, y))
        self.play_effect(screen)

    def play_effect(self, screen: Surface):
        if 0 < self.hurt_duration_effect:
            i = 6 - self.hurt_duration_effect
            hurt_img = self.hurt_effect.subsurface((96 * i, 0), (96, 96))
            hurt_img = pygame.transform.scale(hurt_img, (24, 24))
            cur_position = self.get_position()
            screen.blit(hurt_img, (cur_position.x+12, cur_position.y+12))

    def receive_hurt(self, hurt_hp: int) -> int:
        result = super(TankBase, self).receive_hurt(hurt_hp)
        if result and not self.is_dead:
            self.hurt_duration_effect = 6
        return result

    def update(self, frame_physical_rate: int, screen: Surface) -> None:
        if self.is_dead:
            self.dead_duration_time -= 1
            if 0 > self.dead_duration_time:
                self.need_remove = True
        else:
            self.cool_move_next = max(0, self.cool_move_next-1)
            self.tick_by_per_frame()
            for weapon in self.weapons:
                weapon.tick_by_per_frame()
            if 0 < self.hurt_duration_effect:
                self.hurt_duration_effect -= 1
        self.draw(screen)

    def _get_bullet_shoot_position(self) -> Position:
        """
        子弹开火时的位置
        :return:
        """
        return get_tar_position(self.get_position(), self.rect.width//2, self.rect.height//2)

    def shoot(self) -> Optional[Bullet]:
        """开火(朝当前方向)"""
        if MoveDirection.NONE != self.direction:
            # 默认使用最近获得武器开火，后续可设计成多武器开火
            start_position = self._get_bullet_shoot_position()
            bullet = self.weapons[-1].shoot(start_position=start_position, direction=self.direction)
            return bullet
        return

    def can_remove(self) -> bool:
        return self.need_remove
